package com.yonyougov.devops.cruxcisupport.appenv;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.yonyougov.devops.cruxcisupport.app.AppDao;
import com.yonyougov.devops.cruxcisupport.app.AppEntity;
import com.yonyougov.devops.cruxcisupport.app.AppService;
import com.yonyougov.devops.cruxcisupport.appenv.dto.*;
import com.yonyougov.devops.cruxcisupport.appenv.vo.AppEnvPageLikeReqVo;
import com.yonyougov.devops.cruxcisupport.appenv.vo.AppEnvPageLikeRespVo;
import com.yonyougov.devops.cruxcisupport.appenv.vo.AppEnvPageVo;
import com.yonyougov.devops.cruxcisupport.appenv.vo.AppEnvVo;
import com.yonyougov.devops.cruxcisupport.appenvconfigitem.AppEnvConfigItemDao;
import com.yonyougov.devops.cruxcisupport.appenvconfigitem.AppEnvConfigItemEntity;
import com.yonyougov.devops.cruxcisupport.appenvconfigitem.AppEnvConfigItemService;
import com.yonyougov.devops.cruxcisupport.appenvport.AppEnvPortEntity;
import com.yonyougov.devops.cruxcisupport.appenvport.AppEnvPortService;
import com.yonyougov.devops.cruxcisupport.appversioninfo.vo.AppVersionInfoVo;
import com.yonyougov.devops.cruxcisupport.component.TemplateEngine;
import com.yonyougov.devops.cruxcisupport.component.UserStore;
import com.yonyougov.devops.cruxcisupport.env.EnvDao;
import com.yonyougov.devops.cruxcisupport.env.EnvEntity;
import com.yonyougov.devops.cruxcisupport.env.EnvService;
import com.yonyougov.devops.cruxcisupport.envconfigitem.EnvConfigItemEntity;
import com.yonyougov.devops.cruxcisupport.envconfigitem.EnvConfigItemService;
import com.yonyougov.devops.cruxcisupport.envversionhistory.EnvVersionHistoryService;
import com.yonyougov.devops.cruxcisupport.k8s.rest.PodBatchDelRestRequest;
import com.yonyougov.devops.cruxcisupport.k8s.rest.service.K8sDeploymentService;
import com.yonyougov.devops.cruxcisupport.roleapp.RoleAppService;
import com.yonyougov.devops.cruxcisupport.roleenv.RoleEnvService;
import com.yonyougov.devops.cruxcisupport.thread.GetAppVersionCallable;
import com.yonyougov.devops.cruxcisupport.thread.HandleEnvVersionHistoryThread;
import com.yonyougov.devops.cruxcisupport.util.CruxDevOpsIdGenerator;
import com.yonyougov.devops.cruxcisupport.util.JSONUtils;
import com.yonyougov.devops.cruxcisupport.version.VersionService;
import com.yonyougov.devops.fwc.api.dto.BaseResponseDto;
import com.yonyougov.devops.fwc.api.enums.BaseErrorCode;
import com.yonyougov.devops.fwc.core.dao.BaseDao;
import com.yonyougov.devops.fwc.core.service.impl.BaseServiceImpl;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.compress.archivers.zip.ZipArchiveEntry;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.servlet.ServletOutputStream;
import java.io.IOException;
import java.io.StringReader;
import java.math.BigInteger;
import java.util.*;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.stream.Collectors;
import java.util.zip.ZipOutputStream;

/**
 * t_app_env服务实现
 */
@Slf4j
@Service
public class AppEnvServiceImpl extends BaseServiceImpl<AppEnvEntity, BigInteger> implements AppEnvService {
    @Autowired
    private AppEnvDao appEnvDao;
    @Autowired
    private EnvService envService;
    @Autowired
    private EnvConfigItemService envConfigItemService;
    @Autowired
    private TemplateEngine templateEngine;
    @Autowired
    private AppDao appDao;
    @Autowired
    private EnvDao envDao;
    @Autowired
    private AppEnvPortDao appEnvPortDao;
    @Autowired
    private AppEnvConfigItemDao appEnvConfigItemDao;
    @Autowired
    private ObjectMapper objectMapper;
    @Autowired
    private RoleEnvService roleEnvService;
    @Autowired
    private RoleAppService roleAppService;
    @Autowired
    private VersionService versionService;
    @Autowired
    private UserStore userStore;
    @Autowired
    private EnvVersionHistoryService envVersionHistoryService;
    @Value("${getAppVersionByEnvUrlSuffix}")
    private String getAppVersionByEnvUrlSuffix;

    @Override
    public BaseDao<AppEnvEntity, BigInteger> getDao() {
        return appEnvDao;
    }

    @Override
    public int countByJoin(AppEnvPageLikeDbParams appEnvEntityVo) {
        return appEnvDao.countByJoin(appEnvEntityVo);
    }

    @Override
    public BaseResponseDto findAppEnvLikePage(AppEnvPageLikeDbParams dbParams) {
        try {
            BaseResponseDto result = BaseResponseDto.getSuccessInstance();
            List<BigInteger> appIds = roleAppService.getCurrentUserAppIds();
            List<BigInteger> envIds = roleEnvService.getCurrentUserEnvIds();
            dbParams.setAppIds(appIds);
            dbParams.setEnvIds(envIds);
            int totalCount = this.countByJoin(dbParams);
            List<AppEnvPageVo> appEnvVoList = this.findAllJoinBy(dbParams);
            result.put("totalCount", totalCount);
            result.put("data", appEnvVoList);
            return result;
        } catch (Exception e) {
            return BaseResponseDto.getFailInstance(BaseErrorCode.DATA_NULL);
        }
    }

    @Override
    public List<AppEnvPageLikeRespVo> findPageLike(AppEnvPageLikeReqVo appEnvPageLikeReqVo) {
        return appEnvDao.findPageLike(appEnvPageLikeReqVo);
    }

    @Override
    public int countByPageLike(AppEnvPageLikeReqVo appEnvPageLikeReqVo) {
        return appEnvDao.countByPageLike(appEnvPageLikeReqVo);
    }

    @Override
    public void deleteAppEnvWithAppendages(AppEnvEntity appEnvEntity) {
        try {
            List<AppEnvEntity> all = appEnvDao.findAllBy(appEnvEntity);
            AppEnvConfigItemEntity appEnvConfigItemEntity = new AppEnvConfigItemEntity();
            AppEnvPortEntity appEnvPortEntity = new AppEnvPortEntity();
            all.forEach(appEnv -> {
                //删除t_app_env_config_item
                appEnvConfigItemEntity.setAppEnvId(appEnv.getId());
                appEnvConfigItemDao.delete(appEnvConfigItemEntity);
                //t_app_env_port
                appEnvPortEntity.setAppEnvId(appEnv.getId());
                appEnvPortDao.delete(appEnvPortEntity);
            });
            appEnvDao.delete(appEnvEntity);
        } catch (Exception e) {
            log.error("catch Exception is : {} ", e.getMessage());
        }
    }

    @Override
    public BaseResponseDto findAppEnvPage(AppEnvPageDbParams dbParams) {
        try {
            BaseResponseDto result = BaseResponseDto.getSuccessInstance();
            List<BigInteger> appIds = roleAppService.getCurrentUserAppIds();
            List<BigInteger> envIds = roleEnvService.getCurrentUserEnvIds();
            dbParams.setAppIds(appIds);
            dbParams.setEnvIds(envIds);
            int totalCount = this.countByAppEnvPage(dbParams);
            List<AppEnvPageVo> appEnvVoList = this.findAppEnvPageBy(dbParams);
            result.put("totalCount", totalCount);
            result.put("data", appEnvVoList);
            return result;
        } catch (Exception e) {
            return BaseResponseDto.getFailInstance(BaseErrorCode.DATA_NULL);
        }
    }

    @Override
    public List<AppEnvPageVo> findAppEnvPageBy(AppEnvPageDbParams dbParams) {
        return appEnvDao.findAppEnvPageBy(dbParams);
    }

    @Override
    public int countByAppEnvPage(AppEnvPageDbParams dbParams) {
        return appEnvDao.countByAppEnvPage(dbParams);
    }

    @Override
    public List<AppEnvPageVo> findAllJoinBy(AppEnvPageLikeDbParams appEnvEntityVo) {
        return appEnvDao.findAllJoinBy(appEnvEntityVo);
    }

    @Override
    public String getDeployFile(String appName, String envName) {
        EnvEntity envEntity = new EnvEntity();
        envEntity.setName(envName);
        EnvEntity env = envDao.findBy(envEntity);
        if (null == env) {
            throw new RuntimeException("环境不存在");
        }
//        List<BigInteger> envIds = null;
//        try {
//            envIds = roleEnvService.getCurrentUserEnvIds();
//        } catch (Exception e) {
//            e.printStackTrace();
//        }
//        if (null == envIds || !envIds.contains(env.getId())) {
//            throw new RuntimeException("当前用户没有当前环境的权限");
//        }
        AppEntity appEntity = new AppEntity();
        appEntity.setApp(appName);
        AppEntity app = appDao.findBy(appEntity);
        if (null == app) {
            throw new RuntimeException("应用不存在");
        }
//        List<BigInteger> appIds = null;
//        try {
//            appIds = roleAppService.getCurrentUserAppIds();
//        } catch (Exception e) {
//            e.printStackTrace();
//        }
//        if (null == appIds || !appIds.contains(app.getId())) {
//            throw new RuntimeException("当前用户没有当前应用的权限");
//        }
        AppEnvEntity appEnvEntity = new AppEnvEntity();
        appEnvEntity.setAppId(app.getId());
        appEnvEntity.setEnvId(env.getId());
        AppEnvEntity appEnv = appEnvDao.findBy(appEnvEntity);
        if (null == appEnv) {
            throw new RuntimeException("应用环境信息未配置");
        }
        if (StringUtils.isBlank(appEnv.getImage())) {
            appEnv.setImage(null);
        }
        AppEnvPortEntity appEnvPortEntity = new AppEnvPortEntity();
        appEnvPortEntity.setAppEnvId(appEnv.getId());
        appEnvPortEntity.setPageAble(false);
        List<AppEnvPortEntity> ports = appEnvPortDao.findAllBy(appEnvPortEntity);
        //获取部署环境变量列表
        AppEnvConfigItemEntity appEnvConfigItemEntity = new AppEnvConfigItemEntity();
        appEnvConfigItemEntity.setAppEnvId(appEnv.getId());
        appEnvConfigItemEntity.setPageAble(false);
        List<AppEnvConfigItemEntity> appEnvConfigItemList = appEnvConfigItemDao.findAllBy(appEnvConfigItemEntity);

        Map<String, Object> params = new HashMap<>();
        params.put("app", app);
        params.put("env", env);
        params.put("appEnv", appEnv);
        params.put("appEnvPortList", ports);
        params.put("appEnvConfigItemList", appEnvConfigItemList);
        return templateEngine.write("deploy.ftl", params);
    }

    @Override
    public String getConfigMap(String envName) {
        EnvEntity envEntity = new EnvEntity();
        envEntity.setName(envName);
        EnvEntity env = envService.findBy(envEntity);
        if (null == env) {
            throw new RuntimeException("环境不存在");
        }
        EnvConfigItemEntity envConfigItemEntity = new EnvConfigItemEntity();
        envConfigItemEntity.setEnvId(env.getId());
        envConfigItemEntity.setPageAble(false);
        List<EnvConfigItemEntity> envConfigItems = envConfigItemService.findAllBy(envConfigItemEntity).getDatas();
        Map<String, Object> params = new HashMap<>();
        params.put("configItems", envConfigItems);
        return templateEngine.write("configMap.ftl", params);
    }

    @Override
    public void downloadEnv(String envName, ServletOutputStream outputStream) {
        String configMap = getConfigMap(envName);
//        AppEnvPageLikeDbParams appEnvEntityDto = new AppEnvPageLikeDbParams();
//        appEnvEntityDto.setEnvName(envName);
//        appEnvEntityDto.setPageAble(false);
//        List<AppEnvPageVo> appEnvVoList = this.findAppEnvLikePage(appEnvEntityDto).get("data");
        AppEnvPageLikeReqVo params = new AppEnvPageLikeReqVo();
        params.setEnvName(envName);
        params.setPageAble(false);
        List<AppEnvPageLikeRespVo> appEnvVoList = appEnvDao.findPageLike(params);
        Map<String, String> deployFile = new HashMap<>();
        deployFile.put("configMap", configMap);
        appEnvVoList.forEach(appEnvVo -> {
            String dfile = getDeployFile(appEnvVo.getAppName(), appEnvVo.getEnvName());
            deployFile.put(appEnvVo.getAppName(), dfile);
        });
        try {
            ZipOutputStream zos = new ZipOutputStream(outputStream);
            //zos.setUseZip64(Zip64Mode.AsNeeded);
            //将每个文件用ZipArchiveEntry封装，使用ZipArchiveOutputStream写到压缩文件
            deployFile.forEach((k, v) -> {
                ZipArchiveEntry zipArchiveEntry = new ZipArchiveEntry(envName + "/" + k + ".yaml");
                try {
                    zos.putNextEntry(zipArchiveEntry);
                    IOUtils.copy(new StringReader(v), zos, "UTF-8");
                } catch (IOException ex) {
                    log.error(ex.getMessage(), ex);
                }
            });
            outputStream.flush();
            zos.close();
        } catch (IOException ex) {
            log.error(ex.getMessage(), ex);
        }
    }

    @Override
    @SneakyThrows
    public String getAppVersionByEnv(String envName) {
        List<AppVersionInfoVo> results = new ArrayList<>();
        AppEnvPageLikeDbParams appEnvEntityDto = new AppEnvPageLikeDbParams();
        appEnvEntityDto.setEnvName(envName);
        appEnvEntityDto.setPageAble(false);
        List<AppEnvPageVo> appEnvVoList = this.findAppEnvLikePage(appEnvEntityDto).get("data");
        List<Future<AppVersionInfoVo>> taskList = new ArrayList<>();
        ExecutorService executorService = Executors.newFixedThreadPool(60);
        appEnvVoList.forEach(appEnvVo -> {
            String appName = appEnvVo.getAppName();
            String serverUrlPrefix = appEnvVo.getServerUrlPrefix();
            String requestUrl = serverUrlPrefix + appName + getAppVersionByEnvUrlSuffix;
            log.info("requestUrl is : {}", requestUrl);
            Future<AppVersionInfoVo> future = executorService.submit(new GetAppVersionCallable(requestUrl, appName));
            taskList.add(future);
        });
        for (Future<AppVersionInfoVo> task : taskList) {
            try {
                while (!task.isDone()) {
                    log.info("task isn't completed");
                }
                AppVersionInfoVo appVersionInfoVo = task.get();
                results.add(appVersionInfoVo);
            } catch (Exception e) {
                e.printStackTrace();
                log.error("get exception is {}", e.getMessage());
            } finally {
                executorService.shutdown();
            }
        }
        return objectMapper.writeValueAsString(results);
    }

    @Override
    public boolean syncData(String sourceEnv, String targetEnv) {
        //AppEnvPageLikeDbParams appEnvEntityDto = new AppEnvPageLikeDbParams();
        //appEnvEntityDto.setEnvName(sourceEnv);
        //appEnvEntityDto.setPageAble(false);
        //List<AppEnvPageVo> sourceList = this.findAppEnvLikePage(appEnvEntityDto).get("data");
        //appEnvEntityDto.setEnvName(targetEnv);
        //List<AppEnvPageVo> targetList = this.findAppEnvLikePage(appEnvEntityDto).get("data");

        AppEnvPageDbParams appEnvEntityDto = new AppEnvPageDbParams();
        appEnvEntityDto.setEnvName(sourceEnv);
        appEnvEntityDto.setPageAble(false);
        List<AppEnvPageVo> sourceList = this.findAppEnvPage(appEnvEntityDto).get("data");
        appEnvEntityDto.setEnvName(targetEnv);
        List<AppEnvPageVo> targetList = this.findAppEnvPage(appEnvEntityDto).get("data");
        //获取targetEnvId
        EnvEntity envEntity = new EnvEntity();
        envEntity.setName(targetEnv);
        BigInteger targetEnvId = envDao.findBy(envEntity).getId();
        List<AppEnvEntity> insertList = new ArrayList<>();
        List<AppEnvEntity> updateList = new ArrayList<>();
        for (AppEnvPageVo sourceEntity : sourceList) {
            boolean flag = false;
            String sourceAppName = sourceEntity.getAppName();
            for (AppEnvPageVo tarEntity : targetList) {
                String targetAppName = tarEntity.getAppName();
                if (sourceAppName.equals(targetAppName)) {
                    log.info("sourceAppName is : {};targetAppName is : {}", sourceAppName, targetAppName);
                    if (null != sourceEntity.getPackageVersion() && null != tarEntity.getPackageVersion()) {
                        if (!sourceEntity.getPackageVersion().trim().equals(tarEntity.getPackageVersion().trim())) {
                            tarEntity.setPackageVersion(sourceEntity.getPackageVersion());
                            AppEnvEntity appEnvEntity = new AppEnvEntity();
                            BeanUtils.copyProperties(tarEntity, appEnvEntity);
                            updateList.add(appEnvEntity);
                        }
                    }
                    flag = true;
                    break;
                }
            }
            if (!flag) {
                AppEnvEntity newAppEnvEntity = new AppEnvEntity();
                BeanUtils.copyProperties(sourceEntity, newAppEnvEntity);
                //newAppEnvEntity.setId(new CruxDevOpsIdGenerator().generatorId(envDao));
                newAppEnvEntity.setId(new CruxDevOpsIdGenerator().generatorId(appEnvDao));
                newAppEnvEntity.setEnvId(targetEnvId);
                insertList.add(newAppEnvEntity);
            }
        }
        insertList.forEach(appEnvEntity -> appEnvDao.insert(appEnvEntity));
        updateList.forEach(appEnvEntity -> appEnvDao.update(appEnvEntity));
        return true;
    }

    @Override
    public AppEnvVo findByEnvNameAndAppName(String envName, String appName) {
        return appEnvDao.findByEnvNameAndAppName(envName, appName);
    }

    @Autowired
    private AppEnvConfigItemService appEnvConfigItemService;

    @Autowired
    private AppEnvPortService appEnvPortService;

    @Autowired
    private AppService appService;

    @Autowired
    private K8sDeploymentService k8sDeploymentService;

    @Transactional
    @Override
    public void batchDeleteAppEnvWithAppendages(AppEnvBatchDeleteDto appEnvBatchDeleteDto) throws Exception {
        // 删除前检测，如果已经部署，返回异常
        List<AppEnvEntity> appEnvList = appEnvDao.findById(appEnvBatchDeleteDto.getIds());
        if (appEnvList.size() == appEnvBatchDeleteDto.getIds().size()) {

        } else {
            throw new Exception(String.format("t_app_env检测到脏数据，ids={%s},查出的数据={%s}", JSONUtils.toJSONString(appEnvBatchDeleteDto.getIds()), JSONUtils.toJSONString(appEnvList)));
        }
        EnvEntity envEntity = new EnvEntity();
        envEntity.setId(appEnvList.get(0).getEnvId());
        envEntity = envService.findBy(envEntity);
        if (Objects.nonNull(envEntity)) {
            List<BigInteger> appIds = appEnvList.stream().map(AppEnvEntity::getAppId).collect(Collectors.toList());
            List<AppEntity> appEntityList = appService.findById(appIds);
            String[] appNameList = new String[appEntityList.size()];
            appEntityList.stream().map(AppEntity::getApp).collect(Collectors.toList()).toArray(appNameList);
            String appName = k8sDeploymentService.findFristDeploy(envEntity.getName(), appNameList);
            if (StringUtils.isNotBlank(appName)) {
                //throw new Exception(String.format("%s服务已经部署，不可删除", appName));
                // 改为先kill然后直接删
                k8sDeploymentService.deleteBatchPod(PodBatchDelRestRequest.builder().envName(envEntity.getName()).appNames(Arrays.asList(appName)).build());
            }
        } else {
            throw new Exception(String.format("检测到脏数据，id为%s的环境不存在", appEnvList.get(0).getEnvId()));
        }

        // -----------删除逻辑开始-----------
        List<BigInteger> needDeleteAppEnvIds = new ArrayList<>();
        List<BigInteger> needDeleteAppEnvConfigItemIds = new ArrayList<>();
        List<BigInteger> needDeleteAppEnvPortIds = new ArrayList<>();

        // 删除app_env
        List<BigInteger> appEnvIds = appEnvBatchDeleteDto.getIds();
        needDeleteAppEnvIds.addAll(appEnvIds);

        for (BigInteger needDeleteAppEnvId : needDeleteAppEnvIds) {
            //删除t_app_env_config_item
            AppEnvConfigItemEntity appEnvConfigItemEntity = new AppEnvConfigItemEntity();
            appEnvConfigItemEntity.setAppEnvId(needDeleteAppEnvId);
            appEnvConfigItemEntity.setPageAble(false);
            List<AppEnvConfigItemEntity> appEnvConfigItemEntityList = appEnvConfigItemService.findAllBy(appEnvConfigItemEntity).getDatas();
            needDeleteAppEnvConfigItemIds.addAll(appEnvConfigItemEntityList.stream().map(AppEnvConfigItemEntity::getId).collect(Collectors.toList()));
            //删除t_app_env_port
            AppEnvPortEntity appEnvPortEntity = new AppEnvPortEntity();
            appEnvPortEntity.setAppEnvId(needDeleteAppEnvId);
            appEnvPortEntity.setPageAble(false);
            List<AppEnvPortEntity> AppEnvPortEntityList = appEnvPortService.findAllBy(appEnvPortEntity).getDatas();
            needDeleteAppEnvPortIds.addAll(AppEnvPortEntityList.stream().map(AppEnvPortEntity::getId).collect(Collectors.toList()));
        }

        // 更新数据库
        appEnvDao.batchDeleteById(needDeleteAppEnvIds);
        appEnvConfigItemService.batchDeleteById(needDeleteAppEnvConfigItemIds);
        appEnvPortService.batchDeleteById(needDeleteAppEnvPortIds);
        // -----------删除逻辑结束-----------
    }


    @Override
    public void updateAppEnv(AppEnvEntity appEnvEntity) {
        update(appEnvEntity);
        HandleEnvVersionHistoryThread handleEnvVersionHistoryThread =
                new HandleEnvVersionHistoryThread(Arrays.asList(appEnvEntity), versionService, userStore, envVersionHistoryService, appService);
        handleEnvVersionHistoryThread.start();
    }

    @Override
    @Transactional
    public void versionInit(VersionInitDto versionInitDto) {
        // 更新版本
        List<VersionInitItemDto> list = versionInitDto.getList();
        List<UpdateByEnvNameAndAppNameDto> updateByEnvNameAndAppNameDtoList = list.stream().map(e -> {
            UpdateByEnvNameAndAppNameDto build = new UpdateByEnvNameAndAppNameDto();
            build.setEnvName(e.getEnvName());
            build.setAppName(e.getAppName());
            build.setPackageVersion(e.getVersion());
            return build;
        }).collect(Collectors.toList());
        updateByEnvNameAndAppName(updateByEnvNameAndAppNameDtoList);

        // 记录版本变更
        List<AppEnvEntity> recordVersionUpd = list.stream().map(e -> {
            EnvEntity envEntity = new EnvEntity();
            envEntity.setName(e.getEnvName());
            envEntity = envService.findBy(envEntity);
            AppEntity appEntity = new AppEntity();
            appEntity.setApp(e.getAppName());
            appEntity = appService.findBy(appEntity);
            return AppEnvEntity.builder().envId(envEntity.getId()).appId(appEntity.getId()).packageVersion(e.getVersion()).build();
        }).collect(Collectors.toList());
        HandleEnvVersionHistoryThread handleEnvVersionHistoryThread =
                new HandleEnvVersionHistoryThread(recordVersionUpd, versionService, userStore, envVersionHistoryService, appService);
        handleEnvVersionHistoryThread.start();
    }

    @Override
    public void updateByEnvNameAndAppName(List<UpdateByEnvNameAndAppNameDto> updateByEnvNameAndAppNameDtoList) {
        for (UpdateByEnvNameAndAppNameDto updateByEnvNameAndAppNameDto : updateByEnvNameAndAppNameDtoList) {
            String envName = updateByEnvNameAndAppNameDto.getEnvName();
            String appName = updateByEnvNameAndAppNameDto.getAppName();
            if (StringUtils.isNotBlank(envName) && StringUtils.isNotBlank(appName)) {
                EnvEntity envEntity = new EnvEntity();
                envEntity.setName(envName);
                envEntity = envService.findBy(envEntity);
                AppEntity appEntity = new AppEntity();
                appEntity.setApp(appName);
                appEntity = appService.findBy(appEntity);

                AppEnvEntity appEnvEntity = findBy(AppEnvEntity.builder().envId(envEntity.getId()).appId(appEntity.getId()).build());
                BigInteger appEnvId = appEnvEntity.getId();
                BeanUtils.copyProperties(updateByEnvNameAndAppNameDto, appEnvEntity);
                appEnvEntity.setId(appEnvId);
                update(appEnvEntity);
            }
        }
    }

    /**
     * 打印端口映射单子
     * @param env 环境名称
     * @return 端口映射单dto
     */
    @Override
    public List<PortBillResponseDto> printPortReport(String env){
        // 查询某个环境的部署配置
        BigInteger envId = envService.findBy(EnvEntity.builder().name(env).build()).getId();
        AppEnvEntity buildAppEnvEntity = AppEnvEntity.builder().envId(envId).build();
        buildAppEnvEntity.setPageAble(false);
        List<AppEnvEntity> appEnvEntityList = appEnvDao.findAllBy(buildAppEnvEntity);
        return appEnvEntityList.stream().map(appEnvEntity -> {
            // 查询应用名称
            AppEntity appEntity = appService.findBy(AppEntity.builder().id(appEnvEntity.getAppId()).build());
            if(null == appEntity){
                try {
                    // TODO不理解为什么不能抛出
                    throw new Exception(String.format("通过应用appId=%s未找到应用，请检查t_appenv表=%s是否是脏数据",appEnvEntity.getAppId(),objectMapper.writeValueAsString(appEnvEntity)));
                } catch (JsonProcessingException e) {
                    e.printStackTrace();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
            String appName = appService.findBy(AppEntity.builder().id(appEnvEntity.getAppId()).build()).getApp();
            // 查询该应用在某个部署组下所有的映射端口
            AppEnvPortEntity buildAppEnvPortEntity = AppEnvPortEntity.builder().appEnvId(appEnvEntity.getId()).build();
            buildAppEnvPortEntity.setPageAble(false);
            List<AppEnvPortEntity> appEnvPortEntityList = appEnvPortService.findAllBy(buildAppEnvPortEntity).getDatas();
            List<HashMap<String, String>> ports = appEnvPortEntityList.stream().map(e -> {
                HashMap<String, String> portMap = new HashMap<>();
                portMap.put(e.getName(), e.getPort() + " -> " + e.getMappingPort());
                return portMap;
            }).collect(Collectors.toList());
            return PortBillResponseDto.builder().appName(appName).ports(ports).build();
        }).collect(Collectors.toList());
    }

    /**
     * 打印某个环境下所有的应用版本
     * @param env 环境名称
     * @return
     */
    @Override
    public Map<String, String> printVersionReport(String env) {
        // 这些通过镜像来部署
        List<String> imagesDeploymentList = Arrays.asList("fap-redis", "fap-mq");
        // 查询某个环境的部署配置
        BigInteger envId = envService.findBy(EnvEntity.builder().name(env).build()).getId();
        AppEnvEntity buildAppEnvEntity = AppEnvEntity.builder().envId(envId).build();
        buildAppEnvEntity.setPageAble(false);
        List<AppEnvEntity> appEnvEntityList = appEnvDao.findAllBy(buildAppEnvEntity);
        HashMap<String, String> appVersions = new HashMap<>();
        for (AppEnvEntity appEnvEntity : appEnvEntityList) {
            String appName = appService.findBy(AppEntity.builder().id(appEnvEntity.getAppId()).build()).getApp();
            if (imagesDeploymentList.contains(appName)) {
                appVersions.put(appEnvEntity.getImage().split(":")[0],appEnvEntity.getImage().split(":")[1]);
            }else{
                appVersions.put(appName,appEnvEntity.getPackageVersion());
            }
        }
        return appVersions;
    }

    /**
     * 获取部署后的内部服务端口
     * 约定: 名称为http的即为服务端口
     * @param appEnvId 部署id
     * @return 部署后的服务端口
     */
    @Override
    public Integer getServiceInnerPort(BigInteger appEnvId) throws Exception {
        AppEnvEntity appEnvEntity = appEnvDao.findBy(AppEnvEntity.builder().id(appEnvId).build());
        AppEnvPortEntity appEnvPortBuilder = AppEnvPortEntity.builder().appEnvId(appEnvId).build();
        appEnvPortBuilder.setPageAble(false);
        List<AppEnvPortEntity> datas = appEnvPortService.findAllBy(appEnvPortBuilder).getDatas();
        Map<String, List<AppEnvPortEntity>> collect = datas.stream().collect(
                Collectors.groupingBy(AppEnvPortEntity::getName)
        );
        if(!collect.containsKey("http") || (collect.containsKey("http") && 1 != collect.get("http").size())){
            throw new Exception("请设置一个名称为http的映射端口");
        }
        return collect.get("http").get(0).getPort();
    }

    /**
     * 获取部署后的外部服务端口
     * 约定: 名称为http的即为服务端口
     * @param appEnvId 部署id
     * @return 部署后的服务端口
     */
    @Override
    public Integer getServiceOutPort(BigInteger appEnvId) throws Exception {
        AppEnvEntity appEnvEntity = appEnvDao.findBy(AppEnvEntity.builder().id(appEnvId).build());
        AppEnvPortEntity appEnvPortBuilder = AppEnvPortEntity.builder().appEnvId(appEnvId).build();
        appEnvPortBuilder.setPageAble(false);
        List<AppEnvPortEntity> datas = appEnvPortService.findAllBy(appEnvPortBuilder).getDatas();
        Map<String, List<AppEnvPortEntity>> collect = datas.stream().collect(
                Collectors.groupingBy(AppEnvPortEntity::getName)
        );
        boolean noContain = !collect.containsKey("http"); //不包含向外部提供服务的端口映射
        boolean containButExistMul = (collect.containsKey("http") && 1 != collect.get("http").size()); //包含向外部提供服务的端口映射，但是存在多个
        boolean containButMapperPortIsNull = (collect.containsKey("http") && null == collect.get("http").get(0).getMappingPort()); //包含向外部提供服务的端口映射，但是映射出的端口为空
        if( noContain || containButExistMul || containButMapperPortIsNull){
            throw new Exception("请设置一个名称为http的映射端口");
        }
        return collect.get("http").get(0).getMappingPort();
    }

    /**
     * 部署检查
     * 必须有服务端口，且如果应用设置了暴漏端口，必须映射出去
     * @param appEnvId 部署id
     */
    @Override
    public void deployCheck(BigInteger appEnvId) throws Exception {
        AppEnvEntity appEnvEntity = appEnvDao.findBy(AppEnvEntity.builder().id(appEnvId).build());
        AppEntity appEntity = appService.findBy(AppEntity.builder().id(appEnvEntity.getAppId()).build());
        // 如果该服务选择暴漏端口那么该服务就必须设置端口映射，且映射记录中的内部端口必须为该服务的内部服务端口：前端80后端8080
        // 是否设置端口映射在getServiceInnerPort方法有判断, 未设置抛异常
        if (appEntity.getExposePort() && !getServiceInnerPort(appEnvId).equals(appService.getServiceInnerPort(appEnvEntity.getAppId()))) {
            throw new Exception(String.format("%s服务为暴漏端口模式，请为其设置名称为http，内部端口为%s的端口映射",appEntity.getApp(),appService.getServiceInnerPort(appEnvEntity.getAppId())));
        }
    }
}
